home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 April: Mac OS SDK / Dev.CD Apr 96 SDK / Dev.CD Apr 96 SDK2.toast / Development Kits (Disc 2) / QuickTime / Programming Stuff / Documentation / develop articles / develop Issue 24 / Printing Compressed Images / JPEG Print with Dataload / PrintJPEG with dataload.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-08-19  |  34.2 KB  |  1,270 lines  |  [TEXT/MPCC]

  1. /*
  2.  * PrintJPEG.c
  3.  
  4.     Authored by: David Gelphman 6/9/95. Borrowed liberally from the JFIF-PICT
  5.     conversion program provided as a sample program in the QuickTime development 
  6.     portion of the MacOS SDK.
  7.     
  8.     This is essentially the same as the PrintJPEG application but allows for a QuickTime
  9.     data loading function to be used instead of loading all the data into memory.
  10.     
  11.     Copyright (c) Apple Computer Incorporated. All rights reserved.
  12. */
  13. #include "PrintJPEG with dataload.h"
  14.  
  15. /* globals  */
  16.  
  17. WindowPtr gTheWindow;
  18. JPEGImage gTheJPEGImage;
  19. THPrint    gPrinterRecord = NULL;
  20. ICMDataProcRecord    gTheICMDataProcRecord;
  21.  
  22. /* main */
  23. SInt16 main()
  24. {
  25. OSErr theErr;
  26. DataLoadPtr myDataLoadPtr; 
  27.  
  28.     // initialize the managers and the menus
  29.     if (initMacintosh() != noErr)
  30.         return 0;
  31.     
  32.     /*  allocate a print record for us */
  33.     gPrinterRecord = (THPrint)NewHandle(sizeof( TPrint ) );
  34.  
  35.     // Get the defaults for the current printer driver.
  36.     if (gPrinterRecord != NULL && MemError() == noErr)
  37.     {
  38.         PrOpen(); 
  39.         PrintDefault( gPrinterRecord ); 
  40.         PrClose();
  41.     }else{
  42.         ParamText("\pCan't allocate Print Record.", "\p", "\p", "\p");
  43.         Alert(kGenericError, NULL);    
  44.         return iMemFullErr;    
  45.     }
  46.         
  47.     // Check to see if there are any errors and report them if they occur.
  48.     if(( theErr = PrError()) != noErr){
  49.         Str255 scratchString;
  50.         NumToString((long)theErr, scratchString);
  51.         ParamText("\pCan't Do PrDefault for some reason.", scratchString, "\p", "\p");
  52.         Alert(kGenericError, NULL);    
  53.     }
  54.  
  55.     // make a new ICMDataProc record for using a DataLoading proc with *our* DataLoading
  56.     // proc 'MyDataLoadingProc'.
  57.     gTheICMDataProcRecord.dataProc = NewICMDataProc(MyDataLoadingProc);
  58.  
  59.     if(gTheICMDataProcRecord.dataProc != NULL && (MemError() == noErr) ){
  60.         
  61.         // make a pointer the size of the DataLoad data structure (defined in our .h file) 
  62.         // and put it into the 'dataRefCon' field of our ICMDataProc structure.
  63.         gTheICMDataProcRecord.dataRefCon = (long)NewPtr(sizeof(DataLoad));
  64.     }
  65.         
  66.     myDataLoadPtr = (DataLoadPtr)gTheICMDataProcRecord.dataRefCon;
  67.     
  68.     if(gTheICMDataProcRecord.dataRefCon != NULL && (MemError() == noErr) ){
  69.         // make a new handle the size of our buffersize and put it into our DataLoad structure.
  70.         myDataLoadPtr->theDataBufferH = NewHandle(kMYBUFFERSIZE);
  71.         
  72.         // fill in the size field too.
  73.         myDataLoadPtr->theBufferSize = kMYBUFFERSIZE;
  74.         // initialize the refnum field to zero.
  75.         myDataLoadPtr->theRefNum = 0;
  76.     }
  77.  
  78.     // make sure we could get the memory for our buffer
  79.     if(myDataLoadPtr->theDataBufferH == NULL || MemError() != noErr){
  80.         ParamText("\pCan't allocate Data Loading Buffer", "\p", "\p", "\p");
  81.         Alert(kGenericError, NULL);
  82.         return 1;
  83.     }
  84.  
  85.     
  86.     // initialize our JPEGImage Data Structure.
  87.     gTheJPEGImage.theRefNum = 0;
  88.     SetRect(&gTheJPEGImage.theBounds,0,0,0,0);
  89.     gTheJPEGImage.theDesc = NULL;
  90.  
  91.     // Go ahead and get an image...
  92.     DoTheOpenCommand();
  93.     
  94.     // Process events, etc.
  95.     for (; handleEvent();)
  96.         {
  97.         }    /* for */
  98.     return 1;
  99. } /* main */
  100.     
  101.  
  102. //=====================================================================================
  103. // OSErr DrawJPEGImage(JPEGImage theJPEGImage, Boolean isColorPort)
  104. //=====================================================================================
  105. // Draws a JPEG image, whose data is on disk, to the current port.
  106. //=====================================================================================
  107.  
  108.  
  109. static OSErr DrawJPEGImage(JPEGImage theJPEGImage, Boolean isColorPort)
  110. {
  111.     Rect srcRect;
  112.  
  113.     OSErr theErr = noErr;
  114.             
  115.     DataLoadPtr myDataLoadPtr = (DataLoadPtr)gTheICMDataProcRecord.dataRefCon; 
  116.     long theBufferSize = myDataLoadPtr->theBufferSize;
  117.     short theRefNum = theJPEGImage.theRefNum;
  118.  
  119.     Handle theDataHandle = myDataLoadPtr->theDataBufferH;
  120.     Ptr    theDataPtr ;
  121.     long theAmountToRead;
  122.     ICMDataProcRecord *theDataProcRecord;
  123.  
  124.     ImageDescriptionHandle theDesc = theJPEGImage.theDesc;
  125.     
  126.     if (theDataHandle != NULL){
  127.         PixMapHandle dPixMap = ((CGrafPtr)qd.thePort)->portPixMap;
  128.  
  129.         char hState = HGetState(theDataHandle);
  130.         HLock(theDataHandle);                // Lock down the data before using *theDataHandle
  131.         theDataPtr = StripAddress(*theDataHandle);
  132.             
  133.         // srcRect will be the source rectangle of the image we will draw 
  134.         SetRect(&srcRect, 0, 0, (*theDesc)->width, (*theDesc)->height);            
  135.  
  136.         // set up the data loading procedure, as necessary.
  137.         
  138.         // how much data should we read? It is the smaller of the size of the image or
  139.         // our buffer size.
  140.         theAmountToRead = MIN(theBufferSize, (*theDesc)->dataSize );
  141.         
  142.         // make sure we're at the beginning of the file before we start reading data.
  143.         if(theErr == noErr)theErr = SetFPos(theRefNum, fsFromStart, 0);
  144.         
  145.         // fill up the buffer with the appropriate amt of data. This is either all the
  146.         // data we need or one buffer's worth
  147.         if(theErr == noErr)theErr = FSRead(theRefNum, &theAmountToRead,theDataPtr);
  148.  
  149.         if(theErr == noErr) {
  150.             // if the amount of data read is the same as the size of the image, then
  151.             // we don't need a data loading proc. 
  152.             if(theAmountToRead == (*theDesc)->dataSize){
  153.                 theDataProcRecord = NULL;
  154.                 
  155.                 // this will be used in FDecompressImage or StdPix since we aren't using a
  156.                 // data loading proc.
  157.                 theAmountToRead = 0;
  158.             }else{                
  159.                 // We come here if we need to use a data loading proc. By setting theDataProcRecord
  160.                 // to our ICMDataProcRecord we will use the data loading proc we've set up.
  161.                 
  162.                 theDataProcRecord = &gTheICMDataProcRecord;
  163.             }
  164.             
  165.             if(isColorPort){                    
  166.                 MatrixRecord theMatrix;
  167.                 CQDProcs myStdProcs;
  168.                 StdPixProcPtr MyProcPtr;
  169.                 // set both flag bits described in IM QuickTime p.3-139 for StdPix
  170.                 short flags = kCallOldBits | kCallStdBits;        
  171.     
  172.                 // we need a pixmap for our call to the StdPix bottleneck
  173.                 PixMapHandle SpecialPixMapH = NewPixMap();
  174.                 PixMapPtr SpecialPixMapP;
  175.         
  176.                 if(SpecialPixMapH != NULL && MemError() == noErr){
  177.     
  178.                     MoveHHi((Handle)SpecialPixMapH);
  179.                     HLock((Handle)SpecialPixMapH);
  180.                 
  181.                     SpecialPixMapP = (PixMapPtr)StripAddress(*(Handle)SpecialPixMapH);
  182.                     
  183.                     // make a matrix which describes the mapping between the source
  184.                     // and destination rectangles. In our case, they will be the
  185.                     // same rectangle
  186.     
  187.                     RectMatrix(&theMatrix, &srcRect, &srcRect);
  188.                     
  189.                     // SetCompressedPixMapInfo makes a compressed 'PixMap' structure into SpecialPixMapP, using 
  190.                     // the ImageDescription in 'theDesc' with the compressed image data in 'theDataPtr'
  191.                     
  192.                     // We've set this up so if all the data is in memory then theAmountToRead is zero
  193.                     // and theDataProcRecord is NULL. If we need to use a dataloading proc, then
  194.                     // theAmountToRead is the size of our buffer and theDataProcRecord is our ICMDataProcRecord.
  195.         
  196.                     theErr = SetCompressedPixMapInfo(SpecialPixMapP, theDesc, theDataPtr, theAmountToRead,theDataProcRecord,NULL);
  197.                     
  198.                     if(theErr == noErr){
  199.                         // Look to see if there are custom QuickDraw bottlenecks in the current port.
  200.                         if( (((CGrafPtr)qd.thePort)->grafProcs) == NULL)
  201.                         {
  202.                             // Get the Standard Bottleneck procs. 
  203.                             SetStdCProcs(&myStdProcs);
  204.                             
  205.                             // The 'newProc1' bottleneck is the 'StdPix' bottleneck.
  206.                             MyProcPtr = (StdPixProcPtr)myStdProcs.newProc1;
  207.                             
  208.                         }else{
  209.                             // Use the custom 'StdPix' bottleneck in the grafPort.
  210.                             // The 'newProc1' bottleneck is the 'StdPix' bottleneck.
  211.     
  212.                             MyProcPtr = (StdPixProcPtr)((CGrafPtr)qd.thePort)->grafProcs->newProc1;
  213.                         }
  214.                         
  215.                         // Call the bottleneck. The function call being used here is:
  216.                         // StdPix(SpecialPixMapP, &srcRect, &theMatrix, ditherCopy, NULL, NULL,NULL,flags)
  217.                         // with the appropriate StdPix bottleneck.
  218.                          
  219.                         CallStdPixProc(MyProcPtr,SpecialPixMapP, &srcRect, &theMatrix, 
  220.                             ditherCopy, NULL, NULL,NULL,flags );
  221.                     }
  222.                     HUnlock((Handle)SpecialPixMapH);
  223.                 }
  224.                 if(SpecialPixMapH)DisposePixMap(SpecialPixMapH);
  225.             }else{
  226.                 // Come here for a B&W port since there is no StdPix bottleneck for a B&W grafport. 
  227.                 // By definition there isn't a custom StdPix bottleneck in a B&W grafport. 
  228.                 // We'll let QuickTime figure out how to handle this case. 
  229.                 CodecComponent theCodec;
  230.                 
  231.                 if ( theErr == noErr) {
  232.                     CodecInfo theInfo;
  233.                     
  234.                     // use the bestFidelityCodec if it is available, otherwise just use any
  235.                     theErr = GetCodecInfo(&theInfo, 'jpeg', bestFidelityCodec); 
  236.                 if(theErr == noErr){
  237.                     theCodec = bestFidelityCodec;
  238.                 }else
  239.                     // we know this Codec exists because at startup we checked in the InitMacintosh proc
  240.                     theCodec = anyCodec;    
  241.                 }
  242.     
  243.                 theErr = FDecompressImage(theDataPtr, theDesc, 
  244.                                 dPixMap, &srcRect, NULL, ditherCopy,
  245.                                 NULL, NULL, NULL, codecMaxQuality, theCodec, theAmountToRead,theDataProcRecord, NULL);            
  246.             }
  247.         }
  248.         HSetState(theDataHandle, hState);
  249.         return theErr;
  250.     }
  251. }    /* DrawJPEGImage */
  252.  
  253.  
  254. static void DoTheOpenCommand()
  255. {
  256. OSErr theErr = noErr;
  257. OSType theTypes[] = { 'JPEG' };
  258. StandardFileReply theReply;
  259. Rect bounds;
  260. SInt16 windowTop = GetMBarHeight()+ 19;
  261. SInt16 right,bottom;
  262.  
  263.     // check to see if we already have a file reference. If so, we'll close the file since we're going to open a new one.
  264.     if( gTheJPEGImage.theRefNum){
  265.         FSClose(gTheJPEGImage.theRefNum);
  266.         gTheJPEGImage.theRefNum = 0;
  267.     }
  268.  
  269.     // disable the Print menu item since we can't print data anymore.
  270.     DisableItem(GetMHandle(kFileMenuID), kPrintCommand);
  271.  
  272.     // Hide the existing window, if there is one.
  273.     if(gTheWindow != NULL)HideWindow(gTheWindow);
  274.  
  275.     // Get a JPEG file and read the data.
  276.     if(theErr == noErr){
  277.         StandardGetFile(nil, 1, theTypes, &theReply);
  278.         if (theReply.sfGood) {
  279.             // Open the File so we have a refnum and read it
  280.             theErr = FSpOpenDF(&theReply.sfFile, fsRdPerm, &gTheJPEGImage.theRefNum);    
  281.             
  282.             if(theErr != noErr){
  283.                 ParamText("\pCan't Open the File", "\p", "\p", "\p");
  284.                 Alert(kGenericError, nil);
  285.                 return;    
  286.             }
  287.  
  288.             // Set the file reference number in the myDataLoad structure
  289.             // that is stored in the dataRefCon field of the gTheICMDataProcRecord. We'll use this
  290.             // if we need to use a DataLoading proc for our image.
  291.             ((DataLoadPtr)gTheICMDataProcRecord.dataRefCon)->theRefNum = gTheJPEGImage.theRefNum;
  292.  
  293.         }else {
  294.             // no file selected so just return.
  295.             return;
  296.         };
  297.     };
  298.  
  299.     // Make a JPEG Image Description for the current image.
  300.     if (theErr == noErr) theErr = MakeJPEGImageDescription(&gTheJPEGImage);
  301.     
  302.     if(theErr == iNotJPEGData){
  303.         ParamText(
  304.         "\pThe selected file either doesn't contain JPEG data or a JPEG version that can't be read by this program."
  305.         , "\p", "\p", "\p");
  306.         Alert(kGenericError, NULL);    
  307.  
  308.     }
  309.     
  310.     // Set the bounds data in the JPEGData structure.
  311.     if (theErr == noErr) theErr = SetJPEGBounds(&gTheJPEGImage);
  312.     
  313.     
  314.  
  315.     // Now make a Window of an appropriate size to hold our image. If we already
  316.     // have a window then size it properly.
  317.  
  318.  
  319.     // Max out the Window size at approximately the main screen size.    
  320.     if (theErr == noErr) {
  321.         right = MIN(gTheJPEGImage.theBounds.right , qd.screenBits.bounds.right - kInsetBits );
  322.         bottom = MIN(gTheJPEGImage.theBounds.bottom, qd.screenBits.bounds.bottom - (kInsetBits+windowTop) );
  323.         SetRect(&bounds, 0 ,0 , right, bottom);
  324.     }
  325.     
  326.     if (theErr == noErr){
  327.         
  328.         OffsetRect(&bounds, 0, windowTop);
  329.  
  330.         if(gTheWindow == NULL) {
  331.             gTheWindow = NewCWindow(NULL, &bounds,    
  332.                     (StringPtr)&(theReply.sfFile.name),
  333.                     false, noGrowDocProc, (WindowPtr)-1, true, 0);
  334.             if (gTheWindow == NULL) {
  335.                 theErr = iMemFullErr;    
  336.             }
  337.         } else{
  338.             MoveWindow (gTheWindow, 0, windowTop, true);    
  339.             SetWTitle(gTheWindow, theReply.sfFile.name);
  340.             SizeWindow (gTheWindow, bounds.right - bounds.left, bounds.bottom-bounds.top, true);
  341.         }
  342.     }
  343.     
  344.     if (theErr == noErr) {
  345.         AlignWindow(gTheWindow, true, NULL, NULL);
  346.         ShowWindow(gTheWindow);
  347.         SetPort(gTheWindow);
  348.         OffsetRect(&bounds, 0, -windowTop);
  349.         ClipRect(&bounds);
  350.     };
  351.  
  352.     // Since we have an image, we'll enable the 'Print' menu item.
  353.     
  354.     if (theErr == noErr) {
  355.       EnableItem(GetMHandle(kFileMenuID), kPrintCommand);
  356.     }        
  357.     
  358.     // Invalidate the Window contents so we'll get an update event.
  359.     if( theErr == noErr)InvalRect(&gTheWindow->portRect);
  360.  
  361.     DrawMenuBar();
  362.     
  363.     return;
  364. } /* DoTheOpenCommand */
  365.  
  366. // Initialize the World and check whether the appropriate managers are available.
  367. static OSErr initMacintosh()
  368. {
  369.     long version;
  370.     OSErr theErr = noErr;
  371.        
  372.     InitGraf(&qd.thePort);
  373.     InitFonts();
  374.     InitWindows();
  375.     FlushEvents(everyEvent, 0);
  376.       InitDialogs(NULL);
  377.     TEInit();
  378.     InitCursor();
  379.     SetFractEnable(true);            // I guess this urge comes from my old days at Adobe
  380.     
  381.     theErr = SetupMenus();
  382.     
  383.     if(theErr != noErr){
  384.         ParamText("\pCouldn't load Menus.", "\p", "\p", "\p");
  385.     }
  386.  
  387.     if(theErr == noErr){
  388.         theErr = Gestalt(gestaltSystemVersion, &version);
  389.         if( (theErr != noErr) || (version < 0x0700) ){
  390.                 ParamText("\pThis software requires System 7.0 or higher.", "\p", "\p", "\p");
  391.                 theErr = kAppError;
  392.         }
  393.     }    
  394.  
  395.     if(theErr == noErr){
  396.         theErr = Gestalt(gestaltQuickTimeVersion, &version);
  397.         if( (theErr != noErr) ){
  398.                 ParamText("\pQuickTime not installed.  This demo requires Quicktime.", "\p", "\p", "\p");
  399.         }
  400.     }    
  401.     
  402.     if(theErr == noErr){
  403.         theErr = Gestalt(gestaltCompressionMgr, &version);
  404. //    Checking for version 1.5 of the ICM comes from the JFIF-PICT conversion program 
  405. //    provided as a sample program in the QuickTime section of the SDK. Apparently there
  406. //    are problems with versions prior to version 1.5.    
  407.         if( (theErr != noErr || version < 15) ){
  408.                 if(theErr == noErr) theErr = kAppError;
  409.                 ParamText("\pVersion 1.5 or Greater of the Image Compression Mgr is required.", "\p", "\p", "\p");
  410.         }
  411.     }    
  412.  
  413.     if(theErr == noErr){
  414.         theErr = Gestalt(gestaltComponentMgr, &version);
  415.         if( (theErr != noErr) ){
  416.                 ParamText("\pThe component manager is required but it isn't available.", "\p", "\p", "\p");
  417.         }
  418.     }
  419.     
  420.     if ( theErr == noErr) {        // make sure there is some Codec available that can handle JPEG.
  421.         CodecInfo theInfo;
  422.         
  423.         theErr = GetCodecInfo(&theInfo, 'jpeg', anyCodec);
  424.         if(theErr != noErr)ParamText("\pThere don't appear to be any JPEG decompressors!", "\p", "\p", "\p");        
  425.     }
  426.     
  427.     if(theErr != noErr){
  428.         Alert(kGenericError, NULL);    
  429.     }
  430.     
  431.     return theErr;
  432. }    /* initMacintosh */
  433.  
  434.  
  435. static SInt16 handleEvent()
  436. {
  437.     EventRecord e;
  438.     WindowRecord *w;
  439.     SInt32 s;
  440.     Rect b;
  441.     RgnHandle currentGrayRegion;
  442.     OSErr theErr;
  443.     GrafPtr oldPort;
  444.     
  445.     if (WaitNextEvent(everyEvent, &e, kSleepTime, 0L))
  446.         {
  447.         switch (e.what)
  448.             {
  449.             case mouseDown:
  450.                 switch (FindWindow(e.where, (WindowPtr *)&w))
  451.                     {
  452.                     case inContent:
  453.                         if ((WindowRecord *) FrontWindow() != w)
  454.                             SelectWindow((WindowPtr)w);
  455.                         else if (((WindowRecord *) w)->windowKind < 0)
  456.                             SystemClick(&e, (WindowPtr )w);
  457.                         break;
  458.                     case inDrag:
  459.                         currentGrayRegion = GetGrayRgn();
  460.                         b = (**(RgnHandle)currentGrayRegion).rgnBBox;
  461.                         // make sure drags align well for best performance in redraws.
  462.                         DragAlignedWindow((WindowPtr )w, e.where, &b, NULL, NULL);
  463.                         break;
  464.                     case inGoAway:
  465.                         if (TrackGoAway((WindowPtr )w, e.where)){                        
  466.                             // we should close the file here...
  467.                             if(gTheJPEGImage.theRefNum){
  468.                                 FSClose(gTheJPEGImage.theRefNum);
  469.                                 gTheJPEGImage.theRefNum = 0;
  470.                             }
  471.                             HideWindow((WindowPtr )w);
  472.                             DisableItem(GetMHandle(kFileMenuID), kPrintCommand);
  473.                             DrawMenuBar();
  474.                         }
  475.                         break;
  476.                     case inGrow:
  477.                         SetRect(&b, 30, 30, 0x7fff, 0x7fff);
  478.                         s = GrowWindow((WindowPtr )w, e.where, &b);
  479.                         if (s){
  480.                             SizeWindow((WindowPtr )w, LoWord(s), HiWord(s), 1);
  481.                             SetPort((WindowPtr )w);
  482.                             InvalRect(&gTheWindow->portRect);
  483.                         }
  484.                         break;
  485.                     case inZoomIn:
  486.                         if (TrackBox((WindowPtr )w, e.where, inZoomIn)){
  487.                             ZoomWindow((WindowPtr )w, inZoomIn, 0);
  488.                             SetPort((WindowPtr )w);
  489.                             InvalRect(&gTheWindow->portRect);
  490.                         }
  491.                         break;
  492.                     case inZoomOut:
  493.                         if (TrackBox((WindowPtr )w, e.where, inZoomOut)){
  494.                             ZoomWindow((WindowPtr )w, inZoomOut, 0);
  495.                             SetPort((WindowPtr )w);
  496.                             InvalRect(&gTheWindow->portRect);
  497.                         }
  498.                         break;
  499.                     case inMenuBar: 
  500.                     {
  501.                         SInt32 mResult = MenuSelect(e.where);
  502.                           SInt16 theMenu, theItem;
  503.                           theMenu = HiWord(mResult); 
  504.                           theItem = LoWord(mResult);
  505.                         doCommand(theMenu, theItem);
  506.                           break;
  507.                       }
  508.                 }
  509.                 break;
  510.             case updateEvt:
  511.                 BeginUpdate((WindowPtr)e.message);
  512.                 GetPort(&oldPort);
  513.                 SetPort((WindowPtr)e.message);
  514.                 
  515.                 theErr = DrawJPEGImage(gTheJPEGImage,ISCOLORPORT((WindowPtr)e.message));
  516.  
  517.                 SetPort(oldPort);
  518.                 EndUpdate((WindowPtr)e.message);
  519.  
  520.                 break;
  521.             case keyDown:
  522.               case autoKey:
  523.                 DoKeyDown(&e);
  524.                 break;
  525.               default: break;
  526.  
  527.             }    /* switch */
  528.         }    /* if */
  529.     return 1;
  530. }    /* handleEvent */
  531.  
  532.  
  533. static OSErr SetupMenus(void) 
  534. {
  535.     OSErr theErr = noErr;
  536.     Handle MyMBar;
  537.         
  538.     MyMBar = GetNewMBar(128);
  539.     if(MyMBar) {
  540.         SetMenuBar(MyMBar);
  541.         AddResMenu(GetMHandle(kAppleMenuID),'DRVR');
  542.         DisableItem(GetMHandle(kFileMenuID), kPrintCommand);
  543.         DrawMenuBar();
  544.     } else {
  545.         theErr = kCouldntLoadMenu;
  546.     }
  547.     return theErr;
  548. } /* SetupMenus */
  549.  
  550. static void DoKeyDown(myEvent) 
  551.     EventRecord *myEvent; 
  552. {
  553.     unsigned char charCode = (unsigned char) (myEvent->message & charCodeMask);
  554.     
  555.     if (myEvent->modifiers & cmdKey) 
  556.       { /* command key */
  557.         SInt32 mResult = MenuKey(charCode);
  558.         SInt16 theMenu = HiWord(mResult); 
  559.         SInt16 theItem = LoWord(mResult);
  560.         doCommand(theMenu, theItem);
  561.     }
  562.     return ;
  563. }    /* KeyDown */
  564.  
  565.  
  566. /*
  567.    Display the About dialog.
  568. */
  569. static void showAboutMeDialog() 
  570. {
  571.   GrafPtr savePort;
  572.   SInt16 itemHit;
  573.   DialogPtr theDialog;
  574.  
  575.   GetPort(&savePort);
  576.   theDialog = GetNewDialog(kAboutMeDLOG, NULL, (WindowPtr) -1);
  577.   SetPort(theDialog);
  578.  
  579.   do { ModalDialog(NULL, &itemHit); } while (itemHit != kOKButton);
  580.  
  581.   CloseDialog(theDialog);
  582.  
  583.   SetPort(savePort);
  584.   return;
  585. }    /* showAboutMeDialog */
  586.  
  587. /*
  588.  * Process Menu Selections
  589. */
  590. static void doCommand(SInt16 theMenu,SInt16 theItem)
  591. {
  592. OSErr theErr = noErr ;
  593.  
  594.       switch (theMenu) 
  595.       {
  596.         case kAppleMenuID:
  597.           if (theItem == kAboutMeCommand) 
  598.           {
  599.                 showAboutMeDialog();
  600.           }
  601.           else 
  602.           {
  603.             GrafPtr savePort;
  604.             Str255 daName;
  605.             GetItem(GetMHandle(kAppleMenuID), theItem, daName);
  606.             GetPort(&savePort);
  607.             (void) OpenDeskAcc(daName);
  608.             SetPort(savePort);
  609.           }
  610.           break;
  611.     
  612.         case kFileMenuID:
  613.           switch (theItem) 
  614.           {
  615.             case kOpenCommand:
  616.                 DoTheOpenCommand();
  617.                 break;
  618.             case kSaveCommand:
  619.                 break;
  620.             case kPageSetupCommand:
  621.                                 
  622.                 PrOpen();
  623.                 theErr = PrError();
  624.                     
  625.                 if(theErr == noErr ){
  626.                     PrValidate( gPrinterRecord );
  627.                     theErr = PrError();    
  628.                 }
  629.  
  630.                 if(theErr == noErr){
  631.                     PrStlDialog(gPrinterRecord);
  632.                     theErr = PrError();
  633.                 }
  634.                 PrClose();
  635.                 
  636.                 if(theErr != noErr){
  637.                     Str255 scratchString;
  638.                     NumToString((long)theErr, scratchString);
  639.                     ParamText("\pThere was a print manager error.", scratchString, "\p", "\p");
  640.                     Alert(kGenericError, NULL);    
  641.                 }
  642.                 break;
  643.             case kPrintCommand:
  644.                 theErr = Print(gTheJPEGImage, gPrinterRecord);
  645.                 
  646.                 if(theErr != noErr){
  647.                     Str255 scratchString;
  648.                     NumToString((long)theErr, scratchString);
  649.                     ParamText("\pThere was an error during printing.", scratchString, "\p", "\p");
  650.                     Alert(kGenericError, NULL);    
  651.                 }
  652.                 break;
  653.             case kQuitCommand:
  654.                 ExitToShell();
  655.                 break;
  656.             default:
  657.                 break;
  658.           }
  659.           break;
  660.     
  661.         case kEditMenuID:
  662.           /*
  663.            * Run this through SystemEdit first.
  664.            * SystemEdit will return FALSE if it's not a system window.
  665.            */
  666.           if (SystemEdit(theItem-1)) break;
  667.         
  668.         default:
  669.           break;
  670.         } /* switch theMenu */
  671.     
  672.       HiliteMenu(0);
  673.       return;
  674. }    /* doCommand */
  675.  
  676.  
  677. static OSErr Print(JPEGImage theJPEGImage,  THPrint  thePrintRecord)
  678. {
  679. OSErr theDrawingErr = noErr;
  680. OSErr aPrintingErr = noErr;
  681.  
  682. GrafPtr oldPort;
  683. TPrStatus thePrinterStatus;
  684. TPPrPort PrinterPort;
  685. Boolean OKToPrint;
  686.  
  687.     GetPort( &oldPort ); 
  688.             
  689.     PrOpen();
  690.         
  691.     if( PrError() == noErr){
  692.         PrValidate(thePrintRecord);
  693.         if( PrError() == noErr){
  694.             OKToPrint = PrJobDialog(thePrintRecord);
  695.             if(OKToPrint){
  696.                 PrinterPort = PrOpenDoc(thePrintRecord,NULL,NULL);
  697.                 if(PrError() == noErr){
  698.                     PrOpenPage(PrinterPort,NULL);
  699.                     if(PrError() == noErr){
  700.                         theDrawingErr = DrawJPEGImage(theJPEGImage,ISCOLORPORT((GrafPtr)PrinterPort));
  701.                     }
  702.                     PrClosePage(PrinterPort);
  703.                 }
  704.                 PrCloseDoc(PrinterPort);
  705.                 if( 
  706.                     ( (*thePrintRecord)->prJob.bJDocLoop == bSpoolLoop ) && 
  707.                     ( PrError() == noErr )
  708.                 ){
  709.                     PrPicFile(thePrintRecord,NULL,NULL,NULL, &thePrinterStatus);
  710.                 }
  711.             }
  712.         } 
  713.     }
  714.     aPrintingErr = PrError();
  715.     PrClose();
  716.     SetPort(oldPort);
  717.     return ( (aPrintingErr == noErr) ? theDrawingErr : aPrintingErr) ;
  718. }    /* Print */
  719.  
  720. // This is our data loading procedure to load the data off the disk on an as needed basis.
  721. // It will be called by QuickTime as a result of our StdPix or FDecompress function call.
  722.  
  723. static pascal OSErr MyDataLoadingProc(Ptr *dataP, long bytesNeeded, long refcon)
  724. {
  725.     OSErr theErr = noErr;
  726.     
  727.     if(dataP != NULL){
  728.         DataLoadPtr theDataLoadPtr = (DataLoadPtr)refcon;
  729.         Ptr theDataBufferP = StripAddress(*(theDataLoadPtr->theDataBufferH));
  730.         long theBufferSize = theDataLoadPtr->theBufferSize;
  731.         short theRefNum = theDataLoadPtr->theRefNum;
  732.                 
  733.         // calculate the number of bytes left in our existing data buffer. This is
  734.         // the size of the buffer itself minus the distance we are from the start
  735.         // of the buffer.
  736.  
  737.         long bytesAvail = theBufferSize - (*dataP - theDataBufferP);
  738.         
  739.         // do we already have enough bytes in our buffer for this call to our data loading
  740.         // procedure? If so we don't need to read any additional data.
  741.         if(bytesNeeded > bytesAvail){
  742.         
  743.             // we come here if we don't have enough bytes of data in our buffer. First
  744.             // figure out how many bytes we should read to refill up the buffer.
  745.             long bytesToRead = theBufferSize - bytesAvail;
  746.  
  747.             // if there are bytes available at the end of our buffer then move them
  748.             // to the beginning of the buffer 
  749.             if(bytesAvail) BlockMove(*dataP,theDataBufferP, bytesAvail);
  750.             
  751.             // now go ahead and fill up the remainder of the buffer, starting at the
  752.             // location just after the last valid byte in the buffer
  753.             theErr = FSRead(theRefNum, &bytesToRead, theDataBufferP + bytesAvail);
  754.             
  755.             // ignore end of file errors
  756.             if(theErr == eofErr)theErr = noErr;
  757.             
  758.             // we reset the data pointer used by the caller of the data loading function
  759.             // so that it points to the first byte of valid data, which is the beginning of our buffer.
  760.             *dataP = theDataBufferP;
  761.         }
  762.     }else{
  763.         // Come here for the data mark reset case. The implementation we are providing here
  764.         // doesn't know how to reset the stream so we return an error. I haven't seen
  765.         // a data mark reset as part of JPEG decoding.
  766.         
  767.         // Not handling the data mark reset case slows down PhotoCD significantly.
  768.         // Demonstrating how to do this better will have to wait for another article.
  769.         theErr = -1;
  770.     }
  771.     return theErr;
  772. }
  773.  
  774.  
  775. // SetJPEGBounds sets 'theBounds' field of the JPEG data structure. This relies on the
  776. // ImageDescription field having already been filled in.
  777. static  OSErr SetJPEGBounds(JPEGImage *theJPEGImage)
  778. {
  779.     Rect *theBounds = &(*theJPEGImage).theBounds;
  780.     ImageDescriptionHandle theDesc = (*theJPEGImage).theDesc;
  781.     
  782.     if(theDesc == NULL)return kAppError;
  783.     
  784.     theBounds->top = theBounds->left = 0;
  785.     
  786.     theBounds->right =  (*theDesc)->width;
  787.     theBounds->bottom = (*theDesc)->height;
  788.     return noErr;
  789. }    /* SetJPEGBounds */
  790.  
  791. // MakeJPEGImageDescription creates a QuickTime image description record based on the JPEG data.
  792. static  OSErr MakeJPEGImageDescription(JPEGImage *theJPEGImage) 
  793. {
  794.     OSErr theErr = noErr;
  795.     CodecInfo theInfo;
  796.     short width,height;
  797.     ImageDescriptionHandle desc;
  798.     char    *scanData;
  799.     long    hRes = 72L<<16 , vRes = 72L<<16;
  800.     short    depth = 32;
  801.  
  802.     long    dataAmtToRead,filesize;
  803.     Handle theDataBufferH =((DataLoadPtr)gTheICMDataProcRecord.dataRefCon)->theDataBufferH;
  804.     Ptr theDataPtr;
  805.     
  806.     if((*theJPEGImage).theDesc == NULL){
  807.         (*theJPEGImage).theDesc = (ImageDescriptionHandle)NewHandle(sizeof(ImageDescription));
  808.         if((*theJPEGImage).theDesc == NULL || (MemError() != noErr)){
  809.             theErr = iMemFullErr;
  810.             return theErr;
  811.         }
  812.     }
  813.     
  814.     HLock(theDataBufferH);
  815.     theDataPtr = StripAddress(*theDataBufferH);
  816.     
  817.     // make sure we are pointed to the beginning of the file to read.    
  818.     theErr = SetFPos(theJPEGImage->theRefNum, fsFromStart, 0);
  819.     
  820.     // get the size of the file
  821.     if(theErr == noErr)theErr = GetEOF(theJPEGImage->theRefNum, &filesize);        
  822.     
  823.     // again, make sure we are pointed to the beginning of the file to read.    
  824.     if(theErr == noErr)theErr = SetFPos(theJPEGImage->theRefNum, fsFromStart, 0);
  825.  
  826.     // we either read all the data or a whole buffer full, depending on whether it fits in our buffer.
  827.     dataAmtToRead = MIN(filesize,((DataLoadPtr)gTheICMDataProcRecord.dataRefCon)->theBufferSize);
  828.  
  829.     // go ahead and read the data.
  830.     if(theErr == noErr)theErr = FSRead(theJPEGImage->theRefNum, &dataAmtToRead,theDataPtr);
  831.         
  832.     // Scan the JPEG image data and find the width, height, resolution, and depth of the image.
  833.     
  834.     // Note that MarkerDetect was written to expect that the pointer to the buffer passed in points to
  835.     //  a buffer with all the JPEG data. Here we only have 1 data buffer's worth. This isn't a problem here
  836.     // since 32k (our minimum buffer size) should contain all the information we are scanning for.
  837.  
  838.     // The comment in MarkerDetect that it returns a pointer to the beginning of the image data
  839.     // is misleading in this case. The returned pointer does point to image data, but not all of
  840.     // the data is in memory unless it fits in our buffer size. This isn't a problem for us since
  841.     // we only use the pointer returned from MarkerDetect to see if the data is valid. 
  842.  
  843.     if ( (scanData = MarkerDetect((char *)theDataPtr,&width,&height,&hRes,&vRes,&depth)) == 0 ) {
  844.         theErr = iNotJPEGData;
  845.     }
  846.  
  847.     // This chooses the Codec that can handle JPEG data with the best Fidelity. If that returns an
  848.     // error we'll try and use the best we can find that can handle JPEG. 
  849.     // This will only be used if we are decompressing the data on the host instead
  850.     // of the printer.
  851.     if ( theErr == noErr) {
  852.         theErr = GetCodecInfo(&theInfo, 'jpeg', bestFidelityCodec);
  853.         if(theErr != noErr)
  854.             theErr = GetCodecInfo(&theInfo, 'jpeg', anyCodec);    // we know this exists because we check at 
  855.                                                                 // at App startup in the InitMacintosh proc
  856.     }
  857.     // Set up the ImageDescription based on the JPEG Codec and the JPEG image data.
  858.     if ( theErr == noErr){
  859.         desc =     (*theJPEGImage).theDesc;
  860.         
  861.         (*desc)->idSize = sizeof(ImageDescription);
  862.         (*desc)->cType = 'jpeg';
  863.         (*desc)->resvd1 = (*desc)->resvd2 = (*desc)->dataRefIndex = 0;
  864.         (*desc)->version = theInfo.version;
  865.         (*desc)->revisionLevel = theInfo.revisionLevel;
  866.         (*desc)->vendor = theInfo.vendor;
  867.         (*desc)->temporalQuality = 0;
  868.         (*desc)->spatialQuality = codecMaxQuality;
  869.         (*desc)->width = width;
  870.         (*desc)->height = height;
  871.         (*desc)->hRes = hRes;
  872.         (*desc)->vRes = vRes;
  873.         (*desc)->dataSize = filesize;
  874.         (*desc)->frameCount = 1;
  875.         BlockMove(theInfo.typeName, (*desc)->name, 32);
  876.         (*desc)->depth = depth;
  877.         (*desc)->clutID = -1;
  878.     }
  879.     HUnlock(theDataBufferH);
  880.     return theErr;
  881. } /* MakeJPEGImageDescription */
  882.  
  883.  
  884. /*
  885.  
  886.     JPEG specific stuff. Taken almost literally from the Macintosh OS SDK.
  887.     This is from the JFIF-PICT conversion program provided as a sample program 
  888.     in the QuickTime section of the SDK. The only change was to allow
  889.     JFIF version 1.02 to be used with this application.
  890.     
  891. */
  892.     
  893. /*
  894.  
  895.     Scan the JPEG stream for the proper markers and fill in the image parameters
  896.     
  897.     returns NULL if it can't comprehend the data, otherwise a pointer to the start
  898.     of the JPEG data.
  899.     
  900.     
  901.     It does a cursory check on the JPEG data to see if it's reasonable.
  902.     Check out the ISO JPEG spec if you really want to know what's going on here.
  903.     
  904. */
  905.  
  906. static char *
  907. MarkerDetect(char *data,short *width,short *height,long *hRes,long *vRes,short *depth)
  908. {
  909.     short    frame_field_length;
  910.     short    data_precision;
  911.     short    scan_field_length;
  912.     short    number_component,scan_components;
  913.     short    c1,hv1,q1,c2,hv2,q2,c3,hv3,q3;
  914.     short    dac_t1, dac_t2, dac_t3;
  915.     unsigned char    c;
  916.     short    qtabledefn;
  917.     short    htabledefn;
  918.     short    status;
  919.     short    length;
  920.     short    i;
  921.     
  922.     c = *data++;
  923.     qtabledefn = 0;
  924.     htabledefn = 0;
  925.     status = 0;
  926.     while (c != (unsigned char)MARKER_SOS) {
  927.         while (c != (unsigned char)MARKER_PREFIX)
  928.             c = *data++;                        /* looking for marker prefix bytes */
  929.         while (c == (unsigned char)MARKER_PREFIX)
  930.             c = *data++;                        /* (multiple?) marker prefix bytes */
  931.         if (c == 0)
  932.             continue;                                    /* 0 is never a marker code */
  933.  
  934.         if (c == (unsigned char)MARKER_SOF) {
  935.  
  936.             frame_field_length = *(short *)data;
  937.             data += 2;
  938.             data_precision = *data++;
  939.             
  940.             if ( data_precision != 8 ) { 
  941.                 status = 2;
  942.             }
  943.  
  944.             *height = *(short *)data;
  945.             data += 2;
  946.             *width = *(short *)data;
  947.             data += 2;
  948.                         
  949.             number_component = *data++;
  950.             
  951.             switch ( number_component  ) {
  952.             case 3:
  953.                 c1 = *data++;
  954.                 hv1 = *data++;
  955.                 q1 = *data++;
  956.                 c2 = *data++;
  957.                 hv2 = *data++;
  958.                 q2 = *data++;
  959.                 c3 = *data++;
  960.                 hv3 = *data++;
  961.                 q3 = *data++;
  962.                 *depth = 32;
  963.                 break;
  964.             case 1:        
  965.                 c1 = *data++;
  966.                 hv1 = *data++;
  967.                 q1 = *data++;
  968.                 *depth = 40;
  969.                 break;
  970.             default:
  971.                 status = 3;
  972.                 break;
  973.             }
  974.             continue;
  975.         }
  976.     
  977.         if (c == (unsigned char)MARKER_SOS) {
  978.             short tn;
  979.             scan_field_length = *(short *)data;
  980.             data += 2;
  981.             scan_components = *data++;
  982.             for ( i=0; i < scan_components; i++ ) {
  983.                 unsigned char cn,dac_t;
  984.                 
  985.                 cn = *data++;
  986.                 dac_t = *data++;
  987.                 if ( cn == c1 ) {
  988.                     dac_t1 = dac_t;
  989.                 } else if ( cn == c2 ) {
  990.                     dac_t2 = dac_t;
  991.                 } else if ( cn == c3 ) {
  992.                     dac_t3 = dac_t;
  993.                 } else {    
  994.                     status = 29;
  995.                     break;
  996.                 }
  997.             }
  998.             switch ( tn=(dac_t1 & 0xf) )  {
  999.             case 0:
  1000.             case 1:
  1001.                 break;
  1002.             case 0xf:
  1003.                 break;
  1004.             default:
  1005.                 status = 33;
  1006.                 break;
  1007.             }
  1008.             switch (  tn=(dac_t2 & 0xf) )  {
  1009.             case 0:
  1010.             case 1:
  1011.                 break;
  1012.             case 0xf:
  1013.                 break;
  1014.             default:
  1015.                 status = 33;
  1016.                 break;
  1017.             }
  1018.             switch (  tn=(dac_t3 & 0xf) )  {
  1019.             case 0:
  1020.             case 1:
  1021.                 break;
  1022.             case 0xf:
  1023.                 break;
  1024.             default:
  1025.                 status = 33;
  1026.                 break;
  1027.             }
  1028.  
  1029.  
  1030.             /*  Initialize the DC tables */
  1031.             
  1032.             switch (  tn=dac_t1 & 0xf0 )  {
  1033.             case 0:
  1034.             case 0x10:
  1035.                 break;
  1036.             case 0xf0:
  1037.                 break;
  1038.             default:
  1039.                 status = 34;
  1040.                 break;
  1041.             }
  1042.             switch (  tn=dac_t2 & 0xf0 )  {
  1043.             case 0:
  1044.             case 0x10:
  1045.                 break;
  1046.             case 0xf0:
  1047.                 break;
  1048.             default:
  1049.                 status = 34;
  1050.                 break;
  1051.             }
  1052.             switch (  tn=dac_t3 & 0xf0 )  {
  1053.             case 0:
  1054.             case 0x10:
  1055.                 break;
  1056.             case 0xf0:
  1057.                 break;
  1058.             default:
  1059.                 status = 34;
  1060.                 break;
  1061.             }
  1062.             if ( *data++ != 0 )  {
  1063. //                status = 18;
  1064.             }
  1065.             if ( *data++ != 63 )  {
  1066. //                status = 19;
  1067.             }
  1068.             if ( *data++ != 0 ) {
  1069. //                status = 20;
  1070.             }
  1071.             if ( status )
  1072.                 return(0);
  1073.             else
  1074.                 return(data);
  1075.         }
  1076.  
  1077.         if (c == (unsigned char)MARKER_DQT) {
  1078.             scan_field_length = *(short *)data;
  1079.             SwallowQuantTable(data);
  1080.             data += scan_field_length;
  1081.             continue;
  1082.         }
  1083.         if (c == (unsigned char)MARKER_DHT) {
  1084.             scan_field_length = *(short *)data;
  1085.             SwallowHuffTable(data);
  1086.             continue;
  1087.         }
  1088.         if (c == (unsigned char)MARKER_DRI) {
  1089.             length = *(short *)data;            /* read length */
  1090.             data += 2;
  1091.             length = *(short *)data;            
  1092.             data += 2;
  1093.             continue;
  1094.         }
  1095.         if (c == (unsigned char)MARKER_DNL) {
  1096.             length = *(short *)data;            /* read length */
  1097.             data += 2;
  1098.             length = *(short *)data;            
  1099.             data += 2;
  1100.             continue;
  1101.         }
  1102.         if (c >= (unsigned char)0xD0 && c <= (unsigned char)0xD7) {
  1103.             continue;
  1104.         }
  1105.  
  1106.         if (c == (unsigned char)MARKER_SOI || c == (unsigned char)MARKER_EOI)    /* image start, end marker */
  1107.             continue;
  1108.  
  1109.         if ( (c >= (unsigned char)0xC1 && c <= (unsigned char)0xcF) || (c == (unsigned char)0xde) || (c == (unsigned char)0xdf) ) {
  1110.             status = 12;
  1111.             length = *(short *)data;            /* read length */
  1112.             data += length;
  1113.             continue;
  1114.         }
  1115.         if (c >= (unsigned char)MARKER_APP0 && c <= (unsigned char)0xEF) {
  1116.             length = *(short *)data;            /* read length */
  1117.             data += 2;
  1118.             length -= 2;
  1119.             if ( (c == (unsigned char)MARKER_APP0) && length > 5 ) { /* check for JFIF marker */
  1120.                 char buf[5];
  1121.                 buf[0] = *data++;
  1122.                 buf[1] = *data++;
  1123.                 buf[2] = *data++;
  1124.                 buf[3] = *data++;
  1125.                 buf[4] = *data++;
  1126.                 length -= 5;
  1127.                 
  1128.                 if ( buf[0] == 'J' && buf[1] == 'F'  && buf[2] == 'I'  && buf[3] == 'F' ) {
  1129.                     short    units;
  1130.                     long    xres,yres;
  1131.                     short    version;
  1132.                     
  1133.                     
  1134.                     version = *(short *)data; data += 2;length -= 2;
  1135.                     if ( version != 0x100 && version != 0x101 && version != 0x102 ) { // DMG - added version 102
  1136.                         status = 44;        // unknown JFIF version
  1137.                         break;
  1138.                     }
  1139.                     units = *data++; length--;
  1140.                     xres = *(short *)data; data += 2; length -= 2;
  1141.                     yres = *(short *)data; data += 2; length -= 2;
  1142.  
  1143.                     switch ( units ) {
  1144.                     case 0:            // no res, just aspect ratio
  1145.                         *hRes = FixMul(72L<<16,xres<<16);
  1146.                         *vRes = FixMul(72L<<16,yres<<16);
  1147.                         break;
  1148.                     case 1:            // dots per inch
  1149.                         *hRes = xres<<16;
  1150.                         *vRes = yres<<16;
  1151.                         break;
  1152.                     case 2:            // dots per centimeter (we convert to dpi )
  1153.                         *hRes = FixMul(0x28a3d,xres<<16);
  1154.                         *vRes = FixMul(0x28a3d,xres<<16);
  1155.                         break;    
  1156.                     default:
  1157.                         break;
  1158.                     }
  1159.                     xres = *data++; length--;
  1160.                     yres = *data++; length--;
  1161.                     
  1162.                     /* skip JFIF thumbnail */
  1163.                     
  1164.                     xres *= yres;
  1165.                     data += xres*3; length -= xres*3;
  1166.                     
  1167.                     if (  length != 0 ) {
  1168.                         status = 44;        // bad jfif marker
  1169.                         break;
  1170.                     }
  1171.                 }
  1172.             }
  1173.             data += length;
  1174.             continue;
  1175.         }
  1176.         if (c == (unsigned char)MARKER_COM) {
  1177.             length = *(short *)data;            /* read length */
  1178.             data += length;
  1179.             continue;
  1180.         }
  1181.         if (c >= (unsigned char)0xf0 && c <= (unsigned char)0xfd) {
  1182.             length = *(short *)data;            /* read length */
  1183.             data += length;
  1184.             continue;
  1185.         }
  1186.         if ( c == 0x1 )
  1187.             continue;
  1188.         if ( (c >= (unsigned char)0x2 && c <= (unsigned char)0xbF) ) {
  1189.             length = *(short *)data;            /* read length */
  1190.             status = 13;
  1191.             data += length;
  1192.             continue;
  1193.         }
  1194.     }
  1195.     return(0);
  1196. }
  1197.  
  1198.  
  1199. /*
  1200.     Read the quantization table from the JPEG bitstream.
  1201. */
  1202.  
  1203. static void 
  1204. SwallowQuantTable(char *data)
  1205. {
  1206.     long    i;
  1207.     long    length,pm,nm;
  1208.  
  1209.     length = *(short *)data;            /* read length */
  1210.     length -= 2;
  1211.     data += 2;
  1212.     while ( length ) {
  1213.         nm= *data++;                    /* read precision and number */
  1214.         pm = nm>>4;    
  1215.         nm &= 0xf;
  1216.         length--;
  1217.         if ( pm ) {
  1218.             for(i=0;i<64;i++) {
  1219.                 length -= 2;
  1220.                 data += 2;
  1221.             }
  1222.         } else {
  1223.             for(i=0;i<64;i++) {
  1224.                 length--;
  1225.                 data++;
  1226.             }
  1227.         }
  1228.     }    
  1229. }
  1230.  
  1231. /*
  1232.     Read the huffman table from the JPEG bitstream.
  1233. */
  1234.  
  1235. static void 
  1236. SwallowHuffTable(char *data)
  1237. {
  1238.     short    i,tc,id;
  1239.     long    length;
  1240.     
  1241.     unsigned char    bin[17];
  1242.     unsigned char    val[256];
  1243.  
  1244.     bin[0] = 0;
  1245.     length = *(short *)data;            /* read length */
  1246.     data += 2;
  1247.     length -= 2;
  1248.     while ( length ) {
  1249.         id=*data++;                /* read id */
  1250.         length--;
  1251.         if ( id != 0 && id != 1 && id != 0x10 && id != 0x11) {
  1252.             return;
  1253.         }
  1254.         tc = 0;
  1255.         for(i=0;i<16;i++) {
  1256.             length--;
  1257.             tc += (bin[i+1] = *data++);
  1258.         }
  1259.         for (i=0; i < tc; i++ ) {
  1260.             length--;
  1261.             val[i] = *data++;
  1262.         }
  1263.     }
  1264. }
  1265.     
  1266.  
  1267.  
  1268.  
  1269.  
  1270.